import tensorflow as tf
import tensorflow.keras.datasets as datasets
from GoogLeNet.InceptionV3.models import inception_v3 as iv3
import math

#gpus = tf.config.experimental.list_physical_devices('GPU')
#tf.config.experimental.set_memory_growth(gpus, True)

image_height = 28
image_width = 28
channels = 3
EPOCHS = 50
BATCH_SIZE = 8
NUM_CLASSES = 10


def get_model():
    model = iv3.InceptionV3(NUM_CLASSES)
    model.build(input_shape=(None, image_height, image_width, channels))
    model.summary()
    return model


if __name__ == '__main__':
    # 读数据
    train_dataset, test_dataset = datasets.mnist.load_data()
    train_count = len(train_dataset)
    test_count = len(test_dataset)

    # 创建模型
    model = get_model()

    # define loss and optimizer
    loss_object = tf.keras.losses.SparseCategoricalCrossentropy()
    optimizer = tf.keras.optimizers.Adadelta()

    train_loss = tf.keras.metrics.Mean(name='train_loss')
    train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='train_accuracy')

    valid_loss = tf.keras.metrics.Mean(name='valid_loss')
    valid_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='valid_accuracy')


    @tf.function
    def train_step(images, labels):
        with tf.GradientTape() as tape:
            predictions = model(images, include_aux_logits=True, training=True)
            loss_aux = loss_object(y_true=labels, y_pred=predictions.aux_logits)
            loss = 0.5 * loss_aux + 0.5 * loss_object(y_true=labels, y_pred=predictions.logits)
        gradients = tape.gradient(loss, model.trainable_variables)
        optimizer.apply_gradients(grads_and_vars=zip(gradients, model.trainable_variables))

        train_loss(loss)
        train_accuracy(labels, predictions.logits)


    @tf.function
    def valid_step(images, labels):
        predictions = model(images, include_aux_logits=False, training=False)
        v_loss = loss_object(labels, predictions)

        valid_loss(v_loss)
        valid_accuracy(labels, predictions)


    # start training
    for epoch in range(EPOCHS):
        train_loss.reset_states()
        train_accuracy.reset_states()
        valid_loss.reset_states()
        valid_accuracy.reset_states()
        step = 0
        for images, labels in x_train:
            step += 1
            train_step(images, labels)
            print("Epoch: {}/{}, step: {}/{}, loss: {:.5f}, accuracy: {:.5f}".format(epoch + 1,
                                                                                     EPOCHS,
                                                                                     step,
                                                                                     math.ceil(
                                                                                         train_count / BATCH_SIZE),
                                                                                     train_loss.result(),
                                                                                     train_accuracy.result()))

        for valid_images, valid_labels in test_dataset:
            valid_step(valid_images, valid_labels)

        print("Epoch: {}/{}, train loss: {:.5f}, train accuracy: {:.5f}, "
              "valid loss: {:.5f}, valid accuracy: {:.5f}".format(epoch + 1,
                                                                  EPOCHS,
                                                                  train_loss.result(),
                                                                  train_accuracy.result(),
                                                                  valid_loss.result(),
                                                                  valid_accuracy.result()))

    #model.save_weights(filepath=config.save_model_dir, save_format='tf')